using System;

namespace Lucene.Net.Util
{
    /// <summary>
    /// Copyright 2005 The Apache Software Foundation
    ///
    /// Licensed under the Apache License, Version 2.0 (the "License");
    /// you may not use this file except in compliance with the License.
    /// You may obtain a copy of the License at
    ///
    ///     http://www.apache.org/licenses/LICENSE-2.0
    ///
    /// Unless required by applicable law or agreed to in writing, software
    /// distributed under the License is distributed on an "AS IS" BASIS,
    /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    /// See the License for the specific language governing permissions and
    /// limitations under the License.
    /// </summary>

    /// <summary>
    /// Floating point numbers smaller than 32 bits.
    ///
    /// @lucene.internal
    /// </summary>
    public class SmallFloat
    {
        /// <summary>
        /// No instance </summary>
        private SmallFloat()
        {
        }

        /// <summary>
        /// Converts a 32 bit float to an 8 bit float.
        /// <br>Values less than zero are all mapped to zero.
        /// <br>Values are truncated (rounded down) to the nearest 8 bit value.
        /// <br>Values between zero and the smallest representable value
        ///  are rounded up.
        /// </summary>
        /// <param name="f"> the 32 bit float to be converted to an 8 bit float (byte) </param>
        /// <param name="numMantissaBits"> the number of mantissa bits to use in the byte, with the remainder to be used in the exponent </param>
        /// <param name="zeroExp"> the zero-point in the range of exponent values </param>
        /// <returns> the 8 bit float representation </returns>
        public static sbyte FloatToByte(float f, int numMantissaBits, int zeroExp)
        {
            // Adjustment from a float zero exponent to our zero exponent,
            // shifted over to our exponent position.
            int fzero = (63 - zeroExp) << numMantissaBits;
            int bits = BitConverter.ToInt32(BitConverter.GetBytes(f), 0);
            int smallfloat = bits >> (24 - numMantissaBits);
            if (smallfloat <= fzero)
            {
                return (bits <= 0) ? (sbyte)0 : (sbyte)1; // underflow is mapped to smallest non-zero number. -  negative numbers and zero both map to 0 byte
            }
            else if (smallfloat >= fzero + 0x100)
            {
                return -1; // overflow maps to largest number
            }
            else
            {
                return (sbyte)(smallfloat - fzero);
            }
        }

        /// <summary>
        /// Converts an 8 bit float to a 32 bit float. </summary>
        public static float ByteToFloat(sbyte b, int numMantissaBits, int zeroExp)
        {
            // on Java1.5 & 1.6 JVMs, prebuilding a decoding array and doing a lookup
            // is only a little bit faster (anywhere from 0% to 7%)
            if (b == 0)
            {
                return 0.0f;
            }
            int bits = (b & 0xff) << (24 - numMantissaBits);
            bits += (63 - zeroExp) << 24;
            return BitConverter.ToSingle(BitConverter.GetBytes(bits), 0);
        }

        //
        // Some specializations of the generic functions follow.
        // The generic functions are just as fast with current (1.5)
        // -server JVMs, but still slower with client JVMs.
        //

        /// <summary>
        /// floatToByte(b, mantissaBits=3, zeroExponent=15)
        /// <br>smallest non-zero value = 5.820766E-10
        /// <br>largest value = 7.5161928E9
        /// <br>epsilon = 0.125
        /// </summary>
        public static sbyte FloatToByte315(float f)
        {
            int bits = BitConverter.ToInt32(BitConverter.GetBytes(f), 0);
            int smallfloat = bits >> (24 - 3);
            if (smallfloat <= ((63 - 15) << 3))
            {
                return (bits <= 0) ? (sbyte)0 : (sbyte)1;
            }
            if (smallfloat >= ((63 - 15) << 3) + 0x100)
            {
                return -1;
            }
            return (sbyte)(smallfloat - ((63 - 15) << 3));
        }

        /// <summary>
        /// byteToFloat(b, mantissaBits=3, zeroExponent=15) </summary>
        public static float Byte315ToFloat(sbyte b)
        {
            // on Java1.5 & 1.6 JVMs, prebuilding a decoding array and doing a lookup
            // is only a little bit faster (anywhere from 0% to 7%)
            if (b == 0)
            {
                return 0.0f;
            }
            int bits = (b & 0xff) << (24 - 3);
            bits += (63 - 15) << 24;
            return BitConverter.ToSingle(BitConverter.GetBytes(bits), 0);
        }

        /// <summary>
        /// floatToByte(b, mantissaBits=5, zeroExponent=2)
        /// <br>smallest nonzero value = 0.033203125
        /// <br>largest value = 1984.0
        /// <br>epsilon = 0.03125
        /// </summary>
        public static sbyte FloatToByte52(float f)
        {
            int bits = BitConverter.ToInt32(BitConverter.GetBytes(f), 0);
            int smallfloat = bits >> (24 - 5);
            if (smallfloat <= (63 - 2) << 5)
            {
                return (bits <= 0) ? (sbyte)0 : (sbyte)1;
            }
            if (smallfloat >= ((63 - 2) << 5) + 0x100)
            {
                return -1;
            }
            return (sbyte)(smallfloat - ((63 - 2) << 5));
        }

        /// <summary>
        /// byteToFloat(b, mantissaBits=5, zeroExponent=2) </summary>
        public static float Byte52ToFloat(sbyte b)
        {
            // on Java1.5 & 1.6 JVMs, prebuilding a decoding array and doing a lookup
            // is only a little bit faster (anywhere from 0% to 7%)
            if (b == 0)
            {
                return 0.0f;
            }
            int bits = (b & 0xff) << (24 - 5);
            bits += (63 - 2) << 24;
            return BitConverter.ToSingle(BitConverter.GetBytes(bits), 0);
        }
    }
}